
#include "member_BPicker.h"
#include "member_BShape.h"
#include "member_BBillboard.h"

using namespace BWE;

member_BPicker::member_BPicker(BPicker* intersect)
{
	boss = intersect;
	node = 0;
}
member_BPicker::~member_BPicker()
{

}

void member_BPicker::addItem(BPickItem* item)
{
	if (item)
	{
		item->setNode(node);
		for (int i = 0; i < items.size(); i++)
		{
			if (items[i]->ratio() > item->ratio())
			{
				items.insert(i, item);
				return;
			}
		}
		items.append(item);
	}
}

void member_BPicker::pick(const BLine& line, BNode* node)
{
	if (node->visible() == false)
		return;
	if (exclusions.contain(node))
		return;

	BMatrix matrix = node->matrix();
	matrix.inverse();
	BLine localLine = line * matrix;

	this->node = node;
	for (int i = 0; i < node->shapeCount(); i++)
	{
		BShape* shape = node->shape(i);
		if (!shape->visible())
			continue;
		if (exclusions.contain(shape))
			continue;
		pick(localLine, shape);
	}
	this->node = 0;
	return;
}
void member_BPicker::pick(const BLine& line, BShape* shape)
{
	BMatrix matrix = shape->matrix();
	matrix.inverse();
	BLine localLine = line * matrix;

	if (BSphere* sphere = dynamic_cast<BSphere*>(shape))
	{
		pick(localLine, sphere);
		return;
	}
	if (BGeoSphere* geosphere = dynamic_cast<BGeoSphere*>(shape))
	{
		pick(localLine, geosphere);
		return;
	}
	if (BCylinder* cylinder = dynamic_cast<BCylinder*>(shape))
	{
		return pick(localLine, cylinder);
	}
	if (BMesh* mesh = dynamic_cast<BMesh*>(shape))
	{
		return pick(localLine, mesh);
	}
	if (BGeometry* geometry = dynamic_cast<BGeometry*>(shape))
	{
		return pick(localLine, geometry);
	}
	if (BBillboard* billboard = dynamic_cast<BBillboard*>(shape))
	{
		return pick(localLine, billboard);
	}

}
void member_BPicker::pick(const BLine& line, BMesh* mesh)
{
	BVectorArray* vertices = mesh->vertices();
	if (!vertices)
		return;
	BFaceArray* faces = mesh->faces();
	if (!faces)
		return;
	for (int i = 0; i < faces->size(); i++)
	{
		int ia = (*faces)[i].a();
		int ib = (*faces)[i].b();
		int ic = (*faces)[i].c();

		BVector va = (*vertices)[ia];
		BVector vb = (*vertices)[ib];
		BVector vc = (*vertices)[ic];

		float ratio;
		if (line.intersect(va, vb, vc, ratio))
		{
			BVector normal = (vb - va).cross(vc - va);
			normal.normalize();
			BPickItem* item = new BPickItem();
			item->setRatio(ratio);
			item->setShape(mesh);
			item->setNormal(normal);
			item->setIndex(i);
			item->setPoint(line.vertex(ratio));
			addItem(item);
			return;
		}
	}
}
void member_BPicker::pick(const BLine& line, BGeometry* geometry)
{
	const BBox& box = geometry->box();
	if (!line.intersect(box))
		return;

	BVectorArray* vertices = geometry->vertices();
	if (!vertices)
		return;
	for (int i = 0; i < geometry->elementCount(); i++)
	{
		BElement* element = geometry->element(i);
		Primitive primitive = element->primitive();
		BIndexArray* indices = element->indices();
		int first = element->first();
		int count = element->count();
		if (indices)
		{
			if (count == 0)
				count = indices->size();
			if (element->primitive() == Primitive_Triangles)
			{
				for (int i = first; i < count; i += 3)
				{
					int ia = (*indices)[i];
					int ib = (*indices)[i + 1];
					int ic = (*indices)[i + 2];
					const BVector& va = (*vertices)[ia];
					const BVector& vb = (*vertices)[ib];
					const BVector& vc = (*vertices)[ic];
					float ratio;
					if (line.intersect(va, vb, vc, ratio))
					{
						BVector normal = (vb - va).cross(vc - va);
						normal.normalize();
						BPickItem* item = new BPickItem();
						item->setRatio(ratio);
						item->setShape(geometry);
						item->setPoint(line.vertex(ratio));
						item->setNormal(normal);
						item->setIndex(i);
						item->setElement(element);
						addItem(item);
						return;
					}
				}
			}
			if (element->primitive() == Primitive_TriangleStrip)
			{
				for (int i = first; i < count; i++)
				{
					int ia = (*indices)[i];
					int ib = (*indices)[i + 1];
					int ic = (*indices)[i + 2];
					const BVector& va = (*vertices)[ia];
					const BVector& vb = (*vertices)[ib];
					const BVector& vc = (*vertices)[ic];
					float ratio;
					if (line.intersect(va, vb, vc, ratio))
					{
						BVector normal = (vb - va).cross(vc - va);
						normal.normalize();
						BPickItem* item = new BPickItem();
						item->setRatio(ratio);
						item->setShape(geometry);
						item->setPoint(line.vertex(ratio));
						item->setNormal(normal);
						item->setIndex(i);
						item->setElement(element);
						addItem(item);
						return;
					}
				}
			}
			if (element->primitive() == Primitive_Quads)
			{
				for (int i = first; i < count; i += 4)
				{
					int ia = (*indices)[i];
					int ib = (*indices)[i + 1];
					int ic = (*indices)[i + 2];
					int id = (*indices)[i + 3];
					const BVector& va = (*vertices)[ia];
					const BVector& vb = (*vertices)[ib];
					const BVector& vc = (*vertices)[ic];
					const BVector& vd = (*vertices)[id];
					float ratio = 0;
					if (line.intersect(va, vb, vc, vd, ratio))
					{
						BVector normal = (vb - va).cross(vc - va);
						normal.normalize();
						BPickItem* item = new BPickItem();
						item->setRatio(ratio);
						item->setShape(geometry);
						item->setPoint(line.vertex(ratio));
						item->setNormal(normal);
						item->setIndex(i);
						item->setElement(element);
						addItem(item);
						return;
					}
				}
			}
			continue;
		}
		const BPieceArray* pieces = element->pieces();
		if (pieces)
		{
			if (count == 0)
				count = pieces->size();
			if (element->primitive() == Primitive_Triangles)
			{
				for (int i = first; i < count; i += 3)
				{
					int ia = (*pieces)[i].v();
					int ib = (*pieces)[i + 1].v();
					int ic = (*pieces)[i + 2].v();
					const BVector& va = (*vertices)[ia];
					const BVector& vb = (*vertices)[ib];
					const BVector& vc = (*vertices)[ic];
					float ratio;
					if (line.intersect(va, vb, vc, ratio))
					{
						BVector normal = (vb - va).cross(vc - va);
						normal.normalize();
						BPickItem* item = new BPickItem();
						item->setRatio(ratio);
						item->setShape(geometry);
						item->setPoint(line.vertex(ratio));
						item->setNormal(normal);
						item->setIndex(i);
						item->setElement(element);
						addItem(item);
					}
				}
				continue;
			}
			if (element->primitive() == Primitive_Quads)
			{
				for (int i = first; i < count; i += 4)
				{
					int ia = (*pieces)[i].v();
					int ib = (*pieces)[i + 1].v();
					int ic = (*pieces)[i + 2].v();
					int id = (*pieces)[i + 3].v();
					const BVector& va = (*vertices)[ia];
					const BVector& vb = (*vertices)[ib];
					const BVector& vc = (*vertices)[ic];
					const BVector& vd = (*vertices)[id];
					float ratio;
					if (line.intersect(va, vb, vc, vd, ratio))
					{
						BVector normal = (vb - va).cross(vc - va);
						normal.normalize();
						BPickItem* item = new BPickItem();
						item->setRatio(ratio);
						item->setShape(geometry);
						item->setPoint(line.vertex(ratio));
						item->setNormal(normal);
						item->setIndex(i);
						item->setElement(element);
						addItem(item);
						continue;
					}
				}
				continue;
			}

			continue;
		}
		if (element->primitive() == Primitive_Triangles)
		{
			for (int i = first; i < count; i += 3)
			{
				const BVector& va = (*vertices)[i];
				const BVector& vb = (*vertices)[i + 1];
				const BVector& vc = (*vertices)[i + 2];
				float ratio;
				if (line.intersect(va, vb, vc, ratio))
				{
					BVector normal = (vb - va).cross(vc - va);
					normal.normalize();
					BPickItem* item = new BPickItem();
					item->setRatio(ratio);
					item->setShape(geometry);
					item->setPoint(line.vertex(ratio));
					item->setNormal(normal);
					item->setIndex(i);
					item->setElement(element);
					addItem(item);
					continue;
				}
			}
			continue;
		}
		if (element->primitive() == Primitive_TriangleStrip)
		{
			for (int i = first; i < count; i++)
			{
				const BVector& va = (*vertices)[i];
				const BVector& vb = (*vertices)[i + 1];
				const BVector& vc = (*vertices)[i + 2];
				float ratio;
				if (line.intersect(va, vb, vc, ratio))
				{
					BVector normal = (vb - va).cross(vc - va);
					normal.normalize();
					BPickItem* item = new BPickItem();
					item->setRatio(ratio);
					item->setShape(geometry);
					item->setPoint(line.vertex(ratio));
					item->setNormal(normal);
					item->setIndex(i);
					item->setElement(element);
					addItem(item);
					return;
				}
			}
			continue;
		}
		if (element->primitive() == Primitive_Quads)
		{
			for (int i = first; i < count; i += 4)
			{
				const BVector& va = (*vertices)[i];
				const BVector& vb = (*vertices)[i + 1];
				const BVector& vc = (*vertices)[i + 2];
				const BVector& vd = (*vertices)[i + 3];
				float ratio = 0;
				if (line.intersect(va, vb, vc, vd, ratio))
				{
					BVector normal = (vb - va).cross(vc - va);
					normal.normalize();
					BPickItem* item = new BPickItem();
					item->setRatio(ratio);
					item->setShape(geometry);
					item->setPoint(line.vertex(ratio));
					item->setNormal(normal);
					item->setIndex(i);
					item->setElement(element);
					addItem(item);
					return;
				}
			}
			continue;
		}
	}
}
void member_BPicker::pick(const BLine& line, BSphere* sphere)
{
	BVector center = sphere->position();
	BVector perp = line.perpVertex(center);
	float radius = sphere->radius();
	float dist = (perp - center).length();
	if (dist < radius)
	{
		float len = sqrt(radius * radius - dist * dist);
		BVector point = perp - line.vector() * len;
		float ratio = (point - line.start()).length() / line.length();
		BVector normal = point - center;
		normal.normalize();
		BPickItem* item = new BPickItem();
		item->setRatio(ratio);
		item->setShape(sphere);
		item->setPoint(point);
		item->setNormal(normal);
		addItem(item);
		return;

	}
}
void member_BPicker::pick(const BLine& line, BGeoSphere* geosphere)
{
	BVector perp = line.perpVertex(BVector());
	float radius = geosphere->radius();
	float dist = perp.length();
	if (dist < radius)
	{
		float len = sqrt(radius * radius - dist * dist);
		BVector point = perp - line.vector() * len;
		float ratio = (point - line.start()).length() / line.length();
		BVector normal = point;
		normal.normalize();
		BPickItem* item = new BPickItem();
		item->setRatio(ratio);
		item->setShape(geosphere);
		item->setPoint(point);
		item->setNormal(normal);
		addItem(item);
		return;
	}
}
void member_BPicker::pick(const BLine& line, BCylinder* cylinder)
{
	pick(line, (BGeometry*)cylinder);
}
void member_BPicker::pick(const BLine& line, BBillboard* billboard)
{
	const BQuad& quad = billboard_member(billboard)->quad;
	float ratio = 0;
	if (line.intersect(quad.a(), quad.b(), quad.c(), quad.d(), ratio))
	{
		BVector normal = quad.normal();
		BPickItem* item = new BPickItem();
		item->setRatio(ratio);
		item->setShape(billboard);
		item->setPoint(line.vertex(ratio));
		item->setNormal(normal);
		addItem(item);
		return;
	}
}

